/*
 * functions.c
 *
 *  Created on: 2011-11-25
 *      Author: xugp
 */
#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <event.h>
#include "server.h"
#include "list.h"
#include "hashtable.h"
#include "functions.h"

#define min(a,b) a<b?a:b
#define max(a,b) a>b?a:b

static hashtable_t *g_usertable;
static hashtable_t *g_roomtable;
static hashtable_t *funtable;

list_t *g_requestlist;
hashtable_t * g_connectiontable;
list_t * g_connectionlist;



int verbose;

extern struct event_base* base;
extern pthread_mutex_t connlistlock;

extern int totalconncount;
extern int totalrequestcount;

void initfuntable(){
	g_connectiontable=create_hashtable(100000);
	g_connectionlist=list_create();

	g_usertable=create_hashtable(100000);
	g_roomtable=create_hashtable(1000);

	funtable=create_hashtable(100);
	//
	hash_insert("login",do_login,funtable);
	hash_insert("sendmsg",do_sendmsg,funtable);
	hash_insert("logout",do_logout,funtable);
	hash_insert("heart",do_heart,funtable);
	hash_insert("checkuser",do_checkuser,funtable);

	//room functions
	hash_insert("sendroommsg",do_sendroommsg,funtable);
	hash_insert("joinroom",do_joinroom,funtable);
	hash_insert("exitroom",do_exitroom,funtable);
	hash_insert("roomuserlist",do_roomuserlist,funtable);
	hash_insert("roomusercount",do_roomusercount,funtable);



	//系统管理
	hash_insert("sendsysmsg",do_sendsysmsg,funtable);
	hash_insert("roommsg",do_roommsg,funtable);
	hash_insert("killuser",do_killuser,funtable);
	hash_insert("stats",do_stats,funtable);
}

func getfunction(char * cmd){
	return hash_value(cmd,funtable);
}

void sendcmd(int fd,char * cmd, char * content){
	int cmdlen=0,contentlen=0;
	if(cmd!=NULL){
		cmdlen=strlen(cmd);
	}
	if(content!=NULL){
		contentlen=strlen(content);
	}

	if(cmd!=NULL){

		char fds[10]={0};
		sprintf(fds,"%d",fd);
		node_t * connnode=hash_value(fds,g_connectiontable);
		if(connnode!=NULL){

			char * ret=malloc(cmdlen + contentlen + 8);
			if(content==NULL){
				sprintf(ret,"%s\r\n",cmd);
			}else{
				sprintf(ret,"%s %s\r\n",cmd,content);
			}
			/*
			struct sock_ev * ev;
			connection_t * conn=connnode->data;
			ev=conn->ev;
			event_set(ev->write_ev, fd, EV_WRITE, on_write, ret);
			event_base_set(base, ev->write_ev);
			event_add(ev->write_ev, NULL);
			//*/
			int n=write(fd,ret,strlen(ret));
			if(n<0){
				perror("sendcmd<0");
			}
			//printf("发送命令 %s 至 %d len:%d\n",cmd,fd,n);
			free(ret);
		}
	}
	//free(ret);
}

char * getparam(char * param,size_t paramlen,int index,int flag, char * paramstring){
	int i=0;
	int n=strlen(paramstring);

	int idx=0,startidx=0;
	for(i=0;i<n;i++){
		if(*(paramstring+i)==0x20){
			if(idx==index){
				if(flag==0){
					strncpy(param,paramstring+startidx,min(paramlen-1,i-startidx));
				}else{
					strncpy(param,paramstring+startidx,min(paramlen-1,n-startidx));
				}
				break;
			}else{
				idx++;
				i++;
				while(i<n){
					if(*(paramstring+i)==0x20){
						i++;
					}else{
						break;
					}
				}
				startidx=i;
			}
		}
	}
	if(i==n && idx==index){
		if(flag==0){
			strncpy(param,paramstring+startidx,min(paramlen-1,i-startidx));
		}else{
			strncpy(param,paramstring+startidx,min(paramlen-1,n-startidx));
		}
	}
	return param;

}

//退出房间
void exitroom(user_t * user){
	if(user==NULL) return;
	room_t * room=hash_value(user->roomid,g_roomtable);
	memset(user->roomid,0,sizeof(user->roomid));
	if(room!=NULL){
		node_t * roomusernode=room->userlist->head;
		while(roomusernode!=NULL){
			user_t * tmpuser=roomusernode->data;
			if(strcmp(user->userid,tmpuser->userid)==0){
				lock_list(room->userlist);
				node_t * tmp=roomusernode->next;
				list_delete(roomusernode,room->userlist);
				roomusernode=tmp;
				unlock_list(room->userlist);
				if(verbose){
					printf("user %s logoutroom %s\n",user->userid,room->roomid);
				}
			}else{
				//广播此用户退出的消息
				sendcmd(tmpuser->fd,"USEREXITROOM",user->userid);
				roomusernode=roomusernode->next;
			}
		}

		//删除房间，也可以不删除
		if(room->userlist->head==NULL){
			free(room->userlist);
			hash_remove(room->roomid,g_roomtable);
			free(room);
		}
	}
}

connection_t * getconnectionfromfd(int fd){
	char fds[10]={0};
	sprintf(fds,"%d",fd);
	connection_t * conn=NULL;
	node_t * connnode=hash_value(fds,g_connectiontable);
	if(connnode!=NULL){
		conn=connnode->data;
	}
	return conn;
}

void closeconnection(int fd){
	char fds[10]={0};
	sprintf(fds,"%d",fd);

	//printf("closeconnection:close connection: %d\n",fd);

	node_t * connnode=hash_value(fds,g_connectiontable);
	if(connnode!=NULL){
		connection_t * conn=connnode->data;
		user_t * user=hash_value(conn->userid,g_usertable);
		if(user!=NULL){
			exitroom(user);
			hash_remove(user->userid,g_usertable);
			free(user);
		}


		//lock_list(g_connectionlist);
		hash_remove(fds,g_connectiontable);
		event_del(conn->read_ev);
		free(conn->read_ev);
		close(conn->fd);
		free(conn);
		list_delete(connnode,g_connectionlist);
        //unlock_list(g_connectionlist);

	}
}

user_t * getuserfromfd(int fd){
	user_t * user=NULL;
	connection_t * conn=getconnectionfromfd(fd);
	if(conn!=NULL){
		user=hash_value(conn->userid,g_usertable);
	}
	return user;
}

void do_killuser(request_t* pRequest){
	char userid[100]={0};
	getparam(userid,sizeof(userid),0,1,pRequest->content);

	user_t * user=hash_value(userid,g_usertable);
	if(user!=NULL){
		exitroom(user);
		sendcmd(user->fd,"BEKILLED",NULL);
	}
	sendcmd(pRequest->fd,"KILLUSEROK",userid);
	closeconnection(pRequest->fd);
}

void do_checkuser(request_t *pRequest){
	char userid[100]={0};
	getparam(userid,sizeof(userid),0,1,pRequest->content);
	char status[110]={0};
	user_t * user=hash_value(userid,g_usertable);
	if(user!=NULL){
		sprintf(status,"%s online",userid);
	}else{
		sprintf(status,"%s offline",userid);
	}
	sendcmd(pRequest->fd,"CHECKUSER",status);
}

void do_sendsysmsg(request_t *pRequest){
	char userid[100]={0};
	getparam(userid,sizeof(userid),0,0,pRequest->content);
	char content[512]={0};
	getparam(content,sizeof(content),1,1,pRequest->content);
	if(strcmp("*",userid)==0){
		node_t * connnode;
		lock_list(g_connectionlist);
		for(connnode=g_connectionlist->head;connnode!=NULL;connnode=connnode->next){
			connection_t * conn=connnode->data;
			if(conn!=NULL){
				if(conn->userid[0]!='\0'){
					sendcmd(conn->fd,"RECVSYSMSG",content);
				}
			}
		}
		unlock_list(g_connectionlist);
		sendcmd(pRequest->fd,"SENDSYSMSGOK",NULL);
	}else{
		user_t * user=hash_value(userid,g_usertable);
		if(user!=NULL){
			sendcmd(user->fd,"RECVSYSMSG",content);
			sendcmd(pRequest->fd,"SENDSYSMSGOK",NULL);
		}else{
			sendcmd(pRequest->fd,"SENDSYSMSGERROR","User not found!");
		}
	}

}

void do_stats(request_t* pRequest){
	char ret[2048]={0};
    sprintf(ret,"\nconn count:\t%d\nroom count:\t%d\nuser count:\t%d\ntotalconnectcount:\t%d\ntotalrequestcount:\t%d\nrequestcount:\t%d\n",hash_count(g_connectiontable),hash_count(g_roomtable),hash_count(g_usertable),totalconncount,totalrequestcount,g_requestlist->count);
    sendcmd(pRequest->fd,"",ret);
}

void do_heart(request_t* pRequest){
	//不需要做任何处理
}

void do_roommsg(request_t* pRequest){
	char roomid[100]={0};
	char content[512]={0};
	getparam(roomid,sizeof(roomid),0,0,pRequest->content);
	getparam(content,sizeof(content),1,1,pRequest->content);
	room_t * room=hash_value(roomid,g_roomtable);
	if(room!=NULL){
		lock_list(room->userlist);
		node_t * node=room->userlist->head;
		while(node!=NULL){
			user_t * user=node->data;
			sendcmd(user->fd,"ROOMMSG",content);
			node=node->next;
		}
		unlock_list(room->userlist);
	}
}

void do_roomuserlist(request_t* pRequest){
	char roomid[100]={0};
	char * result=NULL;
	getparam(roomid,sizeof(roomid),0,1,pRequest->content);
	room_t * room=hash_value(roomid,g_roomtable);
	if(room){
		result=malloc(room->userlist->count * (100+2));
		memset(result,0,sizeof(result));
		lock_list(room->userlist);
		node_t * node=room->userlist->head;
		if(node!=NULL){
			while(node!=NULL){
				user_t * user=node->data;
				strcat(result,user->userid);
				strcat(result," ");
				node=node->next;
			}
			*(result + strlen(result)-1)='\0'; //去掉最后一个空格
		}
		unlock_list(room->userlist);
	}

	sendcmd(pRequest->fd,"ROOMUSERLIST",result);
	free(result);
}

void do_roomusercount(request_t* pRequest){
	char roomid[100]={0};
	char result[20]={0};
	int n=0;
	getparam(roomid,sizeof(roomid),0,1,pRequest->content);
	room_t * room=hash_value(roomid,g_roomtable);
	if(room!=NULL){

		memset(result,0,sizeof(result));
		lock_list(room->userlist);
		node_t * node=room->userlist->head;
		if(node!=NULL){
			while(node!=NULL){
				n++;
				node=node->next;
			}
		}
		unlock_list(room->userlist);
	}
	sprintf(result,"%d",n);
	sendcmd(pRequest->fd,"ROOMUSERCOUNT",result);
}

void do_exitroom(request_t* pRequest){
	user_t * user=getuserfromfd(pRequest->fd);
	if(user!=NULL){
		exitroom(user);
	}
	sendcmd(pRequest->fd,"EXITROOMOK",NULL);
}


void do_joinroom(request_t* pRequest){
	char roomid[100]={0};
	getparam(roomid,sizeof(roomid),0,0,pRequest->content);

	user_t * user=getuserfromfd(pRequest->fd);
	if(user==NULL){
		sendcmd(pRequest->fd,"JIONROOMERROR","Login First");
		return;
	}

	if(user->roomid[0]!='\0'){
		if(strcmp(roomid,user->roomid)!=0){
			exitroom(user);
		}
	}

	room_t * room=hash_value(roomid,g_roomtable);
	if(room==NULL){
		room=malloc(sizeof(room_t));
		hash_insert(roomid,room,g_roomtable);
		memcpy(user->roomid,roomid,sizeof(user->roomid));
		memcpy(room->roomid,roomid,sizeof(room->roomid));
		room->userlist=list_create();
		list_push_tail(user,room->userlist);
	}else{

		if(strcasecmp(roomid,user->roomid)!=0){
			memcpy(user->roomid,roomid,sizeof(user->roomid));
			list_push_tail(user,room->userlist);
		}

		lock_list(room->userlist);
		node_t * roomusernode;
		for(roomusernode=room->userlist->head;roomusernode!=NULL;roomusernode=roomusernode->next){
			user_t * tmpuser=roomusernode->data;
			if(strcmp(user->userid,tmpuser->userid)!=0){
				//广播此用户进入房间的消息
				sendcmd(tmpuser->fd,"USERJOINROOM",user->userid);
			}
		}
		unlock_list(room->userlist);
	}
	sendcmd(user->fd,"JOINROOMOK",NULL);
}


void do_login(request_t* pRequest){
	char userid[100]={0};
	getparam(userid,sizeof(userid),0,0,pRequest->content);


	char fds[10]={0};
	sprintf(fds,"%d",pRequest->fd);
	node_t * connnode=hash_value(fds,g_connectiontable);
	if(connnode!=NULL){
		connection_t * conn=connnode->data;
		if(strcmp(conn->userid,userid)!=0){
			user_t * olduser=hash_value(conn->userid,g_usertable);
			if(olduser!=NULL){
				exitroom(olduser);
				hash_remove(olduser->userid,g_usertable);
				free(olduser);
			}

			strcpy(conn->userid,userid);
		}else{

		}
	}

	user_t * user=hash_value(userid,g_usertable);
	if(user!=NULL){
		//如果已经存在系统的用户登录的连接和新的不一样，或者所在房间不一样
		if(user->fd!=pRequest->fd){
			pthread_mutex_lock(&connlistlock);
			closeconnection(user->fd);
			pthread_mutex_unlock(&connlistlock);
			user=NULL;
		}
	}

	if(user==NULL){
		user=malloc(sizeof(user_t));
		memset(user,0,sizeof(user_t));
		user->fd=pRequest->fd;
		memcpy(user->userid,userid,sizeof(user->userid));
		hash_insert(userid,user,g_usertable);
	}
	sendcmd(pRequest->fd,"LOGINOK",NULL);
}

void do_logout(request_t* pRequest){
	//printf("用户logout:%d\n",pRequest->fd);
	pthread_mutex_lock(&connlistlock);
	closeconnection(pRequest->fd);
	pthread_mutex_unlock(&connlistlock);
}

void do_sendmsg(request_t* pRequest){
	char touserid[100]={0};
	char content[512]={0};
	getparam(touserid,sizeof(touserid),0,0,pRequest->content);
	getparam(content,sizeof(content),1,1,pRequest->content);

	user_t * fromuser=getuserfromfd(pRequest->fd);
	if(fromuser!=NULL){
		user_t * touser=hash_value(touserid,g_usertable);
		if(touser!=NULL){
			char * ret=malloc(strlen(fromuser->userid) + strlen(touser->userid) + strlen(content) + 8);
			sprintf(ret,"%s %s %s",fromuser->userid,touserid,content);
			sendcmd(touser->fd,"RECVMSG",ret);
			sendcmd(fromuser->fd,"RECVMSG",ret);
			sendcmd(pRequest->fd,"SENDMSGOK",NULL);
			free(ret);
		}else{
			sendcmd(pRequest->fd,"SENDMSGERROR","User not found!");
		}
	}else{
		sendcmd(pRequest->fd,"SENDMSGERROR","You must login first!");
	}
}


void do_sendroommsg(request_t* pRequest){
	char touserid[100]={0};
	char pub[5]={0};
	char content[512]={0};
	getparam(touserid,sizeof(touserid),0,0,pRequest->content);
	getparam(pub,sizeof(pub),1,0,pRequest->content);
	getparam(content,sizeof(content),2,1,pRequest->content);

	node_t * node;
	user_t * fromuser=getuserfromfd(pRequest->fd);
	room_t * room=hash_value(fromuser->roomid,g_roomtable);

	char * ret=malloc(strlen(fromuser->userid) + strlen(touserid) + strlen(pub) + strlen(content) + 8);
	sprintf(ret,"%s %s %s %s",fromuser->userid,touserid,pub,content);
	if(strcmp("*",touserid)==0){
		if(room!=NULL){
			lock_list(room->userlist);
			for(node=room->userlist->head;node!=NULL;node=node->next){
				user_t * user=node->data;
				sendcmd(user->fd,"RECVROOMMSG",ret);
			}
			unlock_list(room->userlist);
			free(ret);
		}
	}else{
		user_t * touser=hash_value(touserid,g_usertable);
		if(touser!=NULL){
			if(strcmp(pub,"pub")==0){
				lock_list(room->userlist);
				for(node=room->userlist->head;node!=NULL;node=node->next){
					user_t * user=node->data;
					sendcmd(user->fd,"RECVROOMMSG",ret);
				}
				unlock_list(room->userlist);
			}else{
				sendcmd(touser->fd,"RECVMSG",ret);
				sendcmd(fromuser->fd,"RECVROOMMSG",ret);
			}
		}
	}
	free(ret);
}


void do_something(request_t* pRequest){
	sendcmd(pRequest->fd,"ERROR","CMD CAN NOT FOUND!");
}
